package com.bss.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.andy.basics.Dao;
import com.bss.dao.ActivityDao;
import com.bss.dao.ActivityTypeDao;
import com.bss.dao.PaymentDao;
import com.bss.dao.PaymentTypeDao;
import com.bss.model.Activity;
import com.bss.model.ActivityType;
import com.bss.model.DailyDepositAndRefund;
import com.bss.model.Payment;
import com.bss.model.PaymentType;

@Service
@Scope("session")
public class DailyDepositAndRefundService extends com.andy.basics.Service<DailyDepositAndRefund> {

	private static final Logger AUDIT = Logger.getLogger("AUDIT." + DailyDepositAndRefundService.class.getName());

	@Autowired
	private ActivityDao activityDao;

	@Autowired
	private ActivityTypeDao activityTypeDao;

	@Autowired
	private PaymentDao paymentDao;

	@Autowired
	private PaymentTypeDao paymentTypeDao;

	private ActivityType activityType;

	@Override
	public Dao<DailyDepositAndRefund> getDao() {
		throw new UnsupportedOperationException();
	}

	@Override
	@Transactional
	public void save(final DailyDepositAndRefund dailyDepositAndRefund) {

		PaymentType paymentType = this.getSecurityDeposit();
		int originalSecurityBalance = paymentType.getBalance();
		paymentType.setBalance(paymentType.getBalance() + dailyDepositAndRefund.getRoomDepositTotal()
				- dailyDepositAndRefund.getRoomRefundTotal());
		paymentType.setLastUpdated(dailyDepositAndRefund.getDate());
		int finalSecurityBalance = paymentType.getBalance();

		this.paymentTypeDao.update(paymentType);

		paymentType = this.getUtensilDeposit();
		int originalUtensilBalance = paymentType.getBalance();
		paymentType.setBalance(paymentType.getBalance() + dailyDepositAndRefund.getUtensilDepositTotal()
				- dailyDepositAndRefund.getUtensilRefundTotal());
		paymentType.setLastUpdated(dailyDepositAndRefund.getDate());
		int finalUtensilBalance = paymentType.getBalance();
		this.paymentTypeDao.update(paymentType);

		Activity activity = new Activity();
		activity.setActivityType(this.getActivityType());
		activity.setActivityDate(dailyDepositAndRefund.getDate());
		activity.setDescription(null);
		this.activityDao.persist(activity);
		DailyDepositAndRefundService.AUDIT.info("DailyDepositAndRefundService: Updating Security Balance from "
				+ originalSecurityBalance + " to " + finalSecurityBalance + ", Utensil Balance from " + originalUtensilBalance
				+ " to " + finalUtensilBalance);
	}

	public Activity getLastActivity() {
		return this.activityDao.findLastActivity(this.getActivityType());
	}

	public ActivityType getActivityType() {
		if (this.activityType == null) {
			this.activityType = this.activityTypeDao.find("name='Daily Deposit & Refund Report'").get(0);
		}
		return this.activityType;
	}

	public List<Payment> getRoomDepositAndRefunds(final Date tillDate) {
		return this.getPayments(this.getSecurityDeposit(), tillDate);
	}

	public List<Payment> getUtensilDepositAndRefunds(final Date tillDate) {
		return this.getPayments(this.getUtensilDeposit(), tillDate);
	}

	public List<Payment> getPayments(PaymentType paymentType, final Date tillDate) {
		Activity lastActivity = this.activityDao.findLastActivity(this.getActivityType());
		if (lastActivity == null) {
			return this.paymentDao.findPayments(paymentType, tillDate);
		}
		return this.paymentDao.findPayments(paymentType, lastActivity.getActivityDate(), tillDate);
	}

	public PaymentType getSecurityDeposit() {
		return this.paymentTypeDao.find("name='Security Deposit'").get(0);
	}

	public PaymentType getUtensilDeposit() {
		return this.paymentTypeDao.find("name='Utensil Deposit'").get(0);
	}

	@Override
	public void reload() {
		this.setModel(null);
	}

	public DailyDepositAndRefund create(Date to) {

		DailyDepositAndRefund dailyDepositAndRefund = new DailyDepositAndRefund();

		Activity activity = this.activityDao.findLastActivityBefore(this.activityType, to);

		Date fromDate = activity == null ? null : activity.getActivityDate();

		dailyDepositAndRefund.setFromDate(fromDate);
		dailyDepositAndRefund.setToDate(to);
		dailyDepositAndRefund.setSecurityDeposit(this.getSecurityDeposit());
		dailyDepositAndRefund.setUtensilDeposit(this.getUtensilDeposit());
		dailyDepositAndRefund.setRoomDepositAndRefunds(this.paymentDao.findPayments(this.getSecurityDeposit(), fromDate, to));
		dailyDepositAndRefund.setUtensilDepositAndRefunds(this.paymentDao.findPayments(this.getUtensilDeposit(), fromDate, to));

		this.doTotal(dailyDepositAndRefund, false);

		return dailyDepositAndRefund;
	}

	public void doTotal(DailyDepositAndRefund dailyDepositAndRefund) {
		this.doTotal(dailyDepositAndRefund, true);
	}

	public void doTotal(DailyDepositAndRefund dailyDepositAndRefund, boolean current) {
		dailyDepositAndRefund.setRoomDepositTotal(0);
		dailyDepositAndRefund.setRoomRefundTotal(0);

		int roomDeposit = 0;
		int roomRefund = 0;

		if (dailyDepositAndRefund.getRoomDepositAndRefunds() != null) {
			for (Payment payment : dailyDepositAndRefund.getRoomDepositAndRefunds()) {
				if (payment.isRefund()) {
					dailyDepositAndRefund.setRoomRefundTotal(payment.getAmount() + dailyDepositAndRefund.getRoomRefundTotal());
					dailyDepositAndRefund.getRoomRefunds().add(payment);
					roomRefund++;
				} else {
					dailyDepositAndRefund.setRoomDepositTotal(payment.getAmount() + dailyDepositAndRefund.getRoomDepositTotal());
					dailyDepositAndRefund.getRoomDeposits().add(payment);
					roomDeposit++;
				}
			}
		}

		dailyDepositAndRefund.setUtensilDepositTotal(0);
		dailyDepositAndRefund.setUtensilRefundTotal(0);

		int utensilDeposit = 0;
		int utensilRefund = 0;

		if (dailyDepositAndRefund.getUtensilDepositAndRefunds() != null) {
			for (Payment payment : dailyDepositAndRefund.getUtensilDepositAndRefunds()) {
				if (payment.isRefund()) {
					dailyDepositAndRefund
							.setUtensilRefundTotal(payment.getAmount() + dailyDepositAndRefund.getUtensilRefundTotal());
					dailyDepositAndRefund.getUtensilRefunds().add(payment);
					utensilRefund++;
				} else {
					dailyDepositAndRefund.setUtensilDepositTotal(payment.getAmount()
							+ dailyDepositAndRefund.getUtensilDepositTotal());
					dailyDepositAndRefund.getUtensilDeposits().add(payment);
					utensilDeposit++;
				}
			}
		}

		dailyDepositAndRefund.setRows(this.getUtils().largest(roomDeposit, roomRefund, utensilDeposit, utensilRefund));

		this.fillEmpty(dailyDepositAndRefund.getRoomDepositAndRefunds(), dailyDepositAndRefund.getRows());
		this.fillEmpty(dailyDepositAndRefund.getRoomDeposits(), dailyDepositAndRefund.getRows());
		this.fillEmpty(dailyDepositAndRefund.getRoomRefunds(), dailyDepositAndRefund.getRows());
		this.fillEmpty(dailyDepositAndRefund.getUtensilDepositAndRefunds(), dailyDepositAndRefund.getRows());
		this.fillEmpty(dailyDepositAndRefund.getUtensilDeposits(), dailyDepositAndRefund.getRows());
		this.fillEmpty(dailyDepositAndRefund.getUtensilRefunds(), dailyDepositAndRefund.getRows());

		dailyDepositAndRefund.setCashBalanceData(this.prepareData(dailyDepositAndRefund, current));
	}

	public void fillEmpty(List<Payment> payments, int max) {
		if (payments.size() < max) {
			for (int i = payments.size() + 1; i <= max; i++) {
				payments.add(new Payment());
			}
		}
	}

	public List<Map<String, String>> prepareData(DailyDepositAndRefund dailyDepositAndRefund) {
		return this.prepareData(dailyDepositAndRefund, true);
	}

	public List<Map<String, String>> prepareData(DailyDepositAndRefund dailyDepositAndRefund, boolean current) {
		List<Map<String, String>> list = new ArrayList<Map<String, String>>();
		Map<String, String> map1 = new HashMap<String, String>();
		map1.put("header", "Room");
		map1.put("deposit", "" + dailyDepositAndRefund.getRoomDepositTotal());
		map1.put("refund", "" + dailyDepositAndRefund.getRoomRefundTotal());
		map1.put("balance", "" + (dailyDepositAndRefund.getRoomDepositTotal() - dailyDepositAndRefund.getRoomRefundTotal()));

		if (current) {
			map1.put("opening", "" + dailyDepositAndRefund.getSecurityDeposit().getBalance());
			map1.put(
					"closing",
					""
							+ (dailyDepositAndRefund.getSecurityDeposit().getBalance()
									+ dailyDepositAndRefund.getRoomDepositTotal() - dailyDepositAndRefund.getRoomRefundTotal()));
		} else {
			map1.put("closing", "" + dailyDepositAndRefund.getSecurityDeposit().getBalance());
			map1.put(
					"opening",
					""
							+ (dailyDepositAndRefund.getSecurityDeposit().getBalance() - (dailyDepositAndRefund
									.getRoomDepositTotal() - dailyDepositAndRefund.getRoomRefundTotal())));
		}
		Map<String, String> map2 = new HashMap<String, String>();
		map2.put("header", "Utensil");
		map2.put("deposit", "" + dailyDepositAndRefund.getUtensilDepositTotal());
		map2.put("refund", "" + dailyDepositAndRefund.getUtensilRefundTotal());
		map2.put("balance", "" + (dailyDepositAndRefund.getUtensilDepositTotal() - dailyDepositAndRefund.getUtensilRefundTotal()));
		if (current) {
			map2.put("opening", "" + dailyDepositAndRefund.getUtensilDeposit().getBalance());
			map2.put(
					"closing",
					""
							+ (dailyDepositAndRefund.getUtensilDeposit().getBalance()
									+ dailyDepositAndRefund.getUtensilDepositTotal() - dailyDepositAndRefund
									.getUtensilRefundTotal()));

		} else {

			map2.put(
					"opening",
					""
							+ (dailyDepositAndRefund.getUtensilDeposit().getBalance() - (dailyDepositAndRefund
									.getUtensilDepositTotal() - dailyDepositAndRefund.getUtensilRefundTotal())));

			map2.put("closing", "" + dailyDepositAndRefund.getUtensilDeposit().getBalance());
		}

		list.add(map1);
		list.add(map2);

		return list;
	}

}
